/**
 *
 */
package com.iiordanov.util;

/**
 * @author Michael A. MacDonald
 *
 */
public class ByteBufferStack {
    public static final int MAX_DEPTH = 20;
    public static final int MAX_SIZE = 1048;
    private byte[] m_buffer;
    private int[] m_offsets;
    private int m_depth;
    private int m_max_depth;
    private int m_max_size;

    public ByteBufferStack(int maxDepth, int maxSize) {
        m_depth = 0;
        m_max_depth = maxDepth;
        m_max_size = maxSize;
        m_offsets = new int[maxDepth];
        m_buffer = new byte[maxSize];
    }

    public ByteBufferStack() {
        this(MAX_DEPTH, MAX_SIZE);
    }

    /**
     *
     * @return Start of the buffer; this value is valid at least until the next call of reserve()
     */
    public byte[] getBuffer() {
        return m_buffer;
    }

    /**
     *
     * @return Offset in getBuffer() of last reserved region
     */
    public int getOffset() {
        return m_offsets[m_depth];
    }

    public int reserve(int count) {
        if (count < 0 || (count + m_max_size < 0))
            throw new IllegalArgumentException("Count must by greater than 0");
        if (m_depth == m_max_depth) {
            m_max_depth *= 2;
            int[] new_offsets = new int[m_max_depth];
            System.arraycopy(m_offsets, 0, new_offsets, 0, m_depth);
            m_offsets = new_offsets;
        }
        int result = m_offsets[m_depth];
        int new_size = result + count;
        m_offsets[m_depth++] = new_size;
        if (new_size > m_max_size) {
            m_max_size = Math.max(2 * m_max_size, new_size);
            byte[] new_buffer = new byte[m_max_size];
            System.arraycopy(m_buffer, 0, new_buffer, 0, result);
            m_buffer = new_buffer;
        }

        return result;
    }

    public void release() {
        if (m_depth < 1) {
            throw new IllegalStateException("release() without reserve()");
        }
        m_depth--;
    }
}
